import { app, ipcMain } from "electron"
import pkg from "node-machine-id"
import { logShutdown, logger } from "./logger"
const { machineIdSync } = pkg

// Import mqtt with proper type handling using dynamic import
let mqtt: any

  // Using async IIFE to load mqtt
;(async () => {
  try {
    // Use dynamic import instead of require
    mqtt = await import("mqtt").then((m) => m.default || m)
    logger.info("[MQTT Service] Successfully loaded MQTT package")
  } catch (e) {
    logger.error("[MQTT Service] Failed to load MQTT package:", e)
    // Create a mock implementation if MQTT package is not available
    mqtt = {
      connect: () => {
        logger.warn("[MQTT Service] Using mock MQTT client")
        return {
          on: () => {},
          publish: () => {},
          subscribe: () => {},
          unsubscribe: () => {},
          end: () => {},
        }
      },
    }
  }
})()

// Define the configuration types
interface MqttConnectOptions {
  keepalive: number
  reconnectPeriod: number
  clean: boolean
  [key: string]: any
}

interface MqttConfig {
  host: string
  port: number
  username: string
  password: string
  clientId: string
  connectOptions: MqttConnectOptions
}

interface MqttBaseConfig {
  [key: string]: MqttConfig
}

// MQTT base configuration for different environments
const mqttBaseURLConfig: MqttBaseConfig = {
  ["development"]: {
    host: "35.95.209.213",
    port: 8834,
    username: "mqttweb",
    password: "X@Ap3jHfh&~",
    clientId: `mqtt_${machineIdSync()}`,
    connectOptions: {
      keepalive: 30,
      reconnectPeriod: 30 * 1000,
      clean: false,
    },
  },
  ["test"]: {
    host: "35.95.209.213",
    port: 8834,
    username: "mqttweb",
    password: "X@Ap3jHfh&~",
    clientId: `mqtt_${machineIdSync()}`,
    connectOptions: {
      keepalive: 30,
      reconnectPeriod: 30 * 1000,
      clean: false,
    },
  },
  ["production"]: {
    host: "44.240.80.74",
    port: 8834,
    username: "mqttweb",
    password: "X@Ap3jHfh&~",
    clientId: `mqtt_${machineIdSync()}`,
    connectOptions: {
      keepalive: 30,
      reconnectPeriod: 30 * 1000,
      clean: false,
    },
  },
}

// MQTT service implementation
export class MqttService {
  private client: any = null
  private isConnected: boolean = false
  private subscriptions: Set<string> = new Set()
  private deviceIdList: string[] = []
  private mode: string = process.env.NODE_ENV || "development"
  private config: MqttConfig
  private isInitialized: boolean = false
  private connectPromise: Promise<boolean> | null = null
  private connectAttempts: number = 0
  private maxConnectAttempts: number = 10

  constructor() {
    this.config = mqttBaseURLConfig[this.mode]
    logger.info(`[MQTT Service] Initialized with mode: ${this.mode}`)

    // Initialize MQTT when first accessed
    this.initializeMqtt()
  }

  /**
   * Initialize the MQTT module asynchronously
   */
  private async initializeMqtt(): Promise<void> {
    if (this.isInitialized) return

    // Check if mqtt module is available
    if (!mqtt) {
      logger.info("[MQTT Service] Waiting for MQTT module to be loaded...")
      // Try again in 1 second
      setTimeout(() => this.initializeMqtt(), 1000)
      return
    }

    this.isInitialized = true
    logger.info("[MQTT Service] MQTT module initialization completed")
  }

  /**
   * Set device ID list for subscription
   */
  setDeviceIdList(deviceIdList: string[]) {
    this.deviceIdList = deviceIdList
    console.log("this.deviceIdList====>", this.deviceIdList)
    logger.info(`[MQTT Service] Device ID list set: ${deviceIdList.join(", ")}`)

    // Subscribe to topics if already connected
    if (this.isConnected && this.deviceIdList.length > 0) {
      console.log("!this.client==>", !this.client)
      console.log("this.deviceIdList.length==>", this.deviceIdList.length)
      this.subscribeToDeviceTopics()
    }
  }

  /**
   * Set the mode (development, test, production)
   */
  setMode(mode: string) {
    if (mqttBaseURLConfig[mode]) {
      this.mode = mode
      this.config = mqttBaseURLConfig[mode]
      logger.info(`[MQTT Service] Mode set to ${mode}`)

      // Reconnect with new configuration if already connected
      if (this.isConnected) {
        this.disconnect()
        this.connect()
      }
    } else {
      logger.error(`[MQTT Service] Invalid mode: ${mode}`)
    }
  }

  /**
   * Connect to MQTT server with retry logic
   */
  connect(): boolean {
    // If already have a connection promise in progress, don't start another
    if (this.connectPromise) {
      logger.info("[MQTT Service] Connection attempt already in progress")
      return true
    }

    // Reset connect attempts counter for new connection cycle
    this.connectAttempts = 0

    // Start the async connection process
    this.connectPromise = this.connectAsync()

    // Return true to indicate the connection process has started
    return true
  }

  /**
   * Async implementation of connect with retry logic
   */
  private async connectAsync(): Promise<boolean> {
    try {
      // Skip if already connected
      if (this.client && this.isConnected) {
        logger.info("[MQTT Service] Already connected")
        this.connectPromise = null
        return true
      }

      // Clean up any existing connection
      if (this.client) {
        this.client.end(true)
        this.client = null
      }

      // Wait for MQTT module to be available
      await this.waitForMqttModule()

      // Check if connect attempt limit has been reached
      if (this.connectAttempts >= this.maxConnectAttempts) {
        logger.error(`[MQTT Service] Failed to connect after ${this.maxConnectAttempts} attempts`)
        this.connectPromise = null
        return false
      }

      this.connectAttempts++

      const { host, port, clientId, username, password, connectOptions } = this.config
      // Use WebSocket protocol to connect
      const url = `ws://${host}:${port}/mqtt`

      logger.info(`[MQTT Service] Connecting to ${url} with client ID ${clientId} (attempt ${this.connectAttempts})`)

      return new Promise<boolean>((resolve) => {
        try {
          this.client = mqtt.connect(url, {
            clientId,
            username,
            password,
            protocolVersion: 5, // MQTT 5.0
            ...connectOptions,
          })

          // Set up connect timeout
          const connectTimeout = setTimeout(() => {
            logger.error("[MQTT Service] Connection timeout")
            if (this.client) {
              try {
                this.client.end(true)
              } catch (e) {
                // Ignore errors
              }
            }
            this.client = null
            this.isConnected = false

            // Retry connection
            this.connectPromise = null
            resolve(false)
            this.retryConnection()
          }, 15000)

          // Set up event handlers
          this.client.on("connect", () => {
            clearTimeout(connectTimeout)
            logger.info("[MQTT Service] Connected to MQTT server")
            this.isConnected = true
            this.connectPromise = null

            // Subscribe to topics based on device ID list
            if (this.deviceIdList.length > 0) {
              this.subscribeToDeviceTopics()
            }

            resolve(true)
          })

          this.client.on("reconnect", () => {
            logger.info("[MQTT Service] Reconnecting to MQTT server")
          })

          this.client.on("close", () => {
            logger.info("[MQTT Service] MQTT connection closed")
            this.isConnected = false

            // Attempt to reconnect if not shutting down
            if (!app.isQuitting && this.client) {
              try {
                this.client.end(false)
              } catch (e) {
                // Ignore errors
              }
              this.client = null
              this.retryConnection()
            }
          })

          this.client.on("offline", () => {
            logger.info("[MQTT Service] MQTT connection offline")
            this.isConnected = false

            // Attempt to reconnect if not shutting down
            if (!app.isQuitting && this.client) {
              try {
                this.client.end(false)
              } catch (e) {
                // Ignore errors
              }
              this.client = null
              this.retryConnection()
            }
          })

          this.client.on("error", (error: any) => {
            logger.error("[MQTT Service] MQTT connection error:", error)

            // Attempt to reconnect if not shutting down
            if (!app.isQuitting && this.client) {
              try {
                this.client.end(true)
              } catch (e) {
                // Ignore errors
              }
              this.client = null
              this.retryConnection()
            }
          })

          this.client.on("message", (topic: string, message: Buffer, packet: any) => {
            try {
              const messageStr = message.toString();

              // 简单的消息处理，减少详细日志
              if (topic.includes('control')) {
                // 针对控制类消息，只打印简单信息，不打印完整消息内容
                logger.info(`[MQTT Service] Received control message on topic ${topic}`);
              } else {
                // 其他类型消息正常记录
                logger.info(`[MQTT Service] Received message on topic ${topic}: ${messageStr.substring(0, 100)}${messageStr.length > 100 ? '...' : ''}`);
              }

              // Forward message to node script via IPC
              ipcMain.emit('mqtt-message', { topic, message: messageStr, packet });
            } catch (error) {
              logger.error('[MQTT Service] Error processing MQTT message:', error);
            }
          })
        } catch (error) {
          logger.error("[MQTT Service] Error connecting to MQTT server:", error)
          this.connectPromise = null
          resolve(false)
          this.retryConnection()
        }
      })
    } catch (error) {
      logger.error("[MQTT Service] Error in connectAsync:", error)
      this.connectPromise = null
      this.retryConnection()
      return false
    }
  }

  /**
   * Wait for MQTT module to be available
   */
  private async waitForMqttModule(): Promise<void> {
    if (mqtt) return

    logger.info("[MQTT Service] Waiting for MQTT module to be loaded...")

    // Wait for up to 10 seconds for mqtt module to be available
    for (let i = 0; i < 10; i++) {
      if (mqtt) return
      await new Promise((resolve) => setTimeout(resolve, 1000))
    }

    throw new Error("MQTT module not loaded after timeout")
  }

  /**
   * Retry connection with exponential backoff
   */
  private retryConnection(): void {
    if (app.isQuitting) return

    const delay = Math.min(30000, 1000 * Math.pow(2, this.connectAttempts))
    logger.info(`[MQTT Service] Will retry connection in ${delay}ms`)

    setTimeout(() => {
      this.connectPromise = this.connectAsync()
    }, delay)
  }

  /**
   * Subscribe to topics based on device ID list
   */
  subscribeToDeviceTopics() {
    if (!this.isConnected || !this.client || this.deviceIdList.length === 0) {
      logger.info("[MQTT Service] Cannot subscribe to topics: not connected or no device IDs")
      return
    }

    try {
      // Clear existing subscriptions
      this.unsubscribeAll()
      this.subscriptions.clear()

      // Subscribe to all device topics
      for (const deviceId of this.deviceIdList) {
        const topic = `device/${deviceId}/control`
        logger.info(`[MQTT Service] Subscribing to topic: ${topic}`)

        this.client.subscribe(topic, { qos: 0 }, (err: any) => {
          if (err) {
            logger.error(`[MQTT Service] Error subscribing to topic ${topic}:`, err)
          } else {
            logger.info(`[MQTT Service] Successfully subscribed to topic: ${topic}`)
            this.subscriptions.add(topic)
          }
        })
      }
    } catch (error) {
      logger.error("[MQTT Service] Error subscribing to topics:", error)
    }
  }

  /**
   * Unsubscribe from all topics
   */
  unsubscribeAll() {
    if (!this.isConnected || !this.client) {
      return
    }

    try {
      // Unsubscribe from all topics
      if (this.subscriptions.size > 0) {
        logger.info(`[MQTT Service] Unsubscribing from ${this.subscriptions.size} topics`)

        // Convert Set to Array before iteration to fix the linter error
        const topics = Array.from(this.subscriptions)
        for (const topic of topics) {
          this.client.unsubscribe(topic, (err: any) => {
            if (err) {
              logger.error(`[MQTT Service] Error unsubscribing from topic ${topic}:`, err)
            } else {
              logger.info(`[MQTT Service] Successfully unsubscribed from topic: ${topic}`)
            }
          })
        }
      }
    } catch (error) {
      logger.error("[MQTT Service] Error unsubscribing from topics:", error)
    }
  }

  /**
   * Publish a message to a topic
   */
  publish(topic: string, message: string, options: any = {}): boolean {
    if (!this.isConnected || !this.client) {
      logger.error("[MQTT Service] Cannot publish message: not connected")
      return false
    }

    try {
      logger.info(`[MQTT Service] Publishing message to topic ${topic}: ${message.substring(0, 100)}${message.length > 100 ? "..." : ""}`)
      this.client.publish(topic, message, { qos: 0, ...options }, (err: any) => {
        if (err) {
          logger.error(`[MQTT Service] Error publishing message to topic ${topic}:`, err)
        } else {
          logger.info(`[MQTT Service] Successfully published message to topic: ${topic}`)
        }
      })
      return true
    } catch (error) {
      logger.error("[MQTT Service] Error publishing message:", error)
      return false
    }
  }

  /**
   * Subscribe to specific topics
   */
  subscribe(topics: string | string[]): boolean {
    if (!this.isConnected || !this.client) {
      logger.error("[MQTT Service] Cannot subscribe to topics: not connected")
      return false
    }

    try {
      // Convert single topic to array
      const topicsArray = Array.isArray(topics) ? topics : [topics]

      for (const topic of topicsArray) {
        logger.info(`[MQTT Service] Subscribing to topic: ${topic}`)

        this.client.subscribe(topic, { qos: 0 }, (err: any) => {
          if (err) {
            logger.error(`[MQTT Service] Error subscribing to topic ${topic}:`, err)
          } else {
            logger.info(`[MQTT Service] Successfully subscribed to topic: ${topic}`)
            this.subscriptions.add(topic)
          }
        })
      }
      return true
    } catch (error) {
      logger.error("[MQTT Service] Error subscribing to topics:", error)
      return false
    }
  }

  /**
   * Unsubscribe from specific topics
   */
  unsubscribe(topics: string | string[]): boolean {
    if (!this.isConnected || !this.client) {
      logger.error("[MQTT Service] Cannot unsubscribe from topics: not connected")
      return false
    }

    try {
      // Convert single topic to array
      const topicsArray = Array.isArray(topics) ? topics : [topics]

      for (const topic of topicsArray) {
        logger.info(`[MQTT Service] Unsubscribing from topic: ${topic}`)

        this.client.unsubscribe(topic, (err: any) => {
          if (err) {
            logger.error(`[MQTT Service] Error unsubscribing from topic ${topic}:`, err)
          } else {
            logger.info(`[MQTT Service] Successfully unsubscribed from topic: ${topic}`)
            this.subscriptions.delete(topic)
          }
        })
      }
      return true
    } catch (error) {
      logger.error("[MQTT Service] Error unsubscribing from topics:", error)
      return false
    }
  }

  /**
   * Disconnect from MQTT server
   */
  disconnect(): boolean {
    logger.info("[MQTT Service] Disconnecting from MQTT server")

    if (this.connectPromise) {
      // If there's a pending connection, clear it
      this.connectPromise = null
    }

    if (!this.client) {
      logger.info("[MQTT Service] No MQTT client to disconnect")
      return true
    }

    try {
      // Unsubscribe from all topics first
      this.unsubscribeAll()

      // End the client connection with a callback to ensure we log the disconnection
      this.client.end(true, () => {
        logger.info("[MQTT Service] MQTT client successfully disconnected")
      })

      // Store reference to client before nullifying it
      const client = this.client

      // Clear client reference and update connection status
      this.client = null
      this.isConnected = false

      // Ensure the callback is executed even if end() doesn't call it (which can happen in some cases)
      setTimeout(() => {
        if (client && !this.client) {
          // If client reference has changed
          logger.info("[MQTT Service] MQTT client disconnect timeout triggered, ensuring cleanup")
        }
      }, 1000)

      return true
    } catch (error) {
      logger.error("[MQTT Service] Error disconnecting from MQTT server:", error)

      // Force cleanup even if error occurred
      this.client = null
      this.isConnected = false

      // Log forced disconnection
      logger.info("[MQTT Service] Forced MQTT client disconnect after error")

      return false
    }
  }

  /**
   * Get the current connection status
   */
  getConnectionStatus(): boolean {
    return this.isConnected
  }

  /**
   * Register a callback for when MQTT connection status changes
   * Returns an unsubscribe function
   */
  onConnectionStatusChange(callback: (connected: boolean) => void): () => void {
    if (!this.client) {
      return () => {}
    }

    const connectHandler = () => callback(true)
    const disconnectHandler = () => callback(false)

    this.client.on("connect", connectHandler)
    this.client.on("close", disconnectHandler)
    this.client.on("offline", disconnectHandler)
    this.client.on("error", disconnectHandler)

    // Return unsubscribe function
    return () => {
      if (this.client) {
        this.client.removeListener("connect", connectHandler)
        this.client.removeListener("close", disconnectHandler)
        this.client.removeListener("offline", disconnectHandler)
        this.client.removeListener("error", disconnectHandler)
      }
    }
  }
}

// Create singleton instance
export const mqttService = new MqttService()

// Make sure to disconnect on app quit
app.on("will-quit", (event) => {
  if (mqttService.getConnectionStatus()) {
    // 阻止app立即退出，等待mqtt断开完成
    event.preventDefault()
    logger.info("[MQTT Service] App is quitting, disconnecting from MQTT server")

    // 先取消所有订阅，然后断开连接
    mqttService.unsubscribeAll()
    mqttService.disconnect()

    // 给一定时间完成断开连接并记录日志
    setTimeout(() => {
      logger.info("[MQTT Service] Continuing app quit after MQTT disconnect")
      app.quit()
    }, 1000)
  }
})

